#ifdef HAVE_CONFIG_H
  #include "config.h"
#endif

#include <gtest4c.h>
#include <gigione.h>
#include <gutil.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>

#include <gigitest.h>

void PiCalc (GIGI_MPI mpi, void * param);
void StreamBcast (GIGI_MPI mpi, void * param);
void TYPE_ARGV (GIGI_MPI mpi, void * param);
void BaseComm (GIGI_MPI mpi, void * param);
void broker_BaseComm (GIGIONE context, GIGI_SOCKET fd, void * param);

void tpsTest (GIGI_MPI mpi, void * param);
void RemoteService (GIGI_MPI mpi, void * param);

GMPI_FUNC_MAP_BEGIN (test_func_map)

GMPI_FUNC_MAP (PiCalc)
GMPI_FUNC_MAP_BROKER (BaseComm)
GMPI_FUNC_MAP (tpsTest)
GMPI_FUNC_MAP (StreamBcast)
GMPI_FUNC_MAP (TYPE_ARGV)
GMPI_FUNC_MAP (RemoteService) 
GMPI_FUNC_MAP_END (test_func_map)

void MPIRegister (GIGIONE context)
{
  gigi_register_mpi_func (context, test_func_map, GIGI_TRUE);
};

MPI_TEST (baseMPI, PiCalc   , 33 , 1);
MPI_TEST (baseMPI, BaseComm , 39 , 1);
MPI_TEST (baseMPI, tpsTest  , 8  , 4000);
MPI_TEST (RemoteCall, RemoteService, 32, 1);
MPI_TEST (types, StreamBcast, 10, 1);
MPI_TEST (types, TYPE_ARGV, 8, 100);

void BaseComm (GIGI_MPI mpi, void * param)
{
  int id = gmpi_rank (mpi);
  int nSize=gmpi_size (mpi);
  int v32;
  int nRoot;

  param;

  if (id == 0)
  {
    v32 = 1000;
  };

  C_ASSERT_TRUE ( sizeof (v32) == gmpi_bcast (mpi, GIGI_TYPE_INT32, &v32, NULL, 0) );
  C_ASSERT_TRUE ( v32 == 1000 );

  if (id == 0)
  {
    nRoot = rand () / ( (RAND_MAX/nSize)+1);
  };

  C_ASSERT_TRUE ( sizeof (v32) == gmpi_bcast (mpi, GIGI_TYPE_INT32, &nRoot, NULL, 0) );

  if (id == nRoot)
  {
    v32 = 1010;
  };

  C_ASSERT_TRUE ( sizeof (v32) == gmpi_bcast (mpi, GIGI_TYPE_INT32, &v32, NULL, nRoot) );
  C_ASSERT_TRUE ( v32 == 1010 );

  v32 = id;
  C_ASSERT_TRUE ( gmpi_reduce (mpi, GIGI_SUM, GIGI_TYPE_INT32, &v32, NULL, nRoot ) >= 0 );

  if (id == nRoot )
  {
    C_ASSERT_TRUE ( v32 == (nSize * (nSize - 1) / 2 ) );
  }
  
  gmpi_bcast (mpi, GIGI_TYPE_INT32, &v32, NULL, nRoot);

  C_ASSERT_TRUE ( v32 == (nSize * (nSize - 1) / 2 ) );

  printf ("%d/%d, BaseComm\n", id, nSize);
};

void PiCalc (GIGI_MPI mpi, void * param)
{
  int id = gmpi_rank (mpi);
  int nSize=gmpi_size (mpi);
  int i, count, hit = 0;
  double fPI;

  param;

  printf ("I am %d (%d)\n", id, nSize);


  if (id  == 0 )
  {
    count = 10000 + rand () / (RAND_MAX/2000);
  };

  C_ASSERT_TRUE ( sizeof (count) == gmpi_bcast (mpi, GIGI_TYPE_INT32, &count, NULL, 0) );

  printf ("count = %d\n", count);

  for ( i =0; i<count; i++)
  {
    int x = rand () / (RAND_MAX/200);
    int y = rand () / (RAND_MAX/200);

    if ( (x * x + y * y) <=  200 * 200 )
    {
      hit++;
    }
  };

  fPI = 4 * (double)hit / (double)count;
  gmpi_reduce (mpi, GIGI_SUM, GIGI_TYPE_DOUBLE, &fPI, NULL, 0);
  fPI /= nSize; 

  if (id == 0)
  {
    printf ("%d count = %d, hit =%d rate=%lf\n", id, count, hit, fPI );
  }
};


void tpsTest (GIGI_MPI mpi, void * param)
{
  int id = 10;
  
  param;

  gmpi_bcast (mpi, GIGI_TYPE_INT32, &id, NULL, 0);
};

void broker_BaseComm (GIGIONE context, GIGI_SOCKET fd, void * param)
{
  GIGI_ID Nodes[128];
  GIGI_ID service_id;
  int nodeSize;

  nodeSize = GIGI_COUNT_OF (Nodes);
  C_ASSERT_TRUE ( gigi_recv_object (fd, GIGI_TYPE_ID | GIGI_TYPE_ARRAY, Nodes, &nodeSize) > 0 );
  C_ASSERT_TRUE ( nodeSize >0 );

  C_ASSERT_TRUE ( gigi_recv_object (fd, GIGI_TYPE_ID, &service_id, NULL) > 0 );

  gmpi_call (context, gigi_mpi_func_def (context, "BaseComm"), Nodes, nodeSize, param, service_id, 5000 );
}

void RemoteService (GIGI_MPI mpi, void * param)
{
  int nSize = gmpi_size (mpi);
  int id    = gmpi_rank (mpi);
  int nOK;
  GIGI_ID service_id = GIGI_ID_MAKE1 (2222);
  GIGI_SERVICE gserv;

  gserv = gigi_open_service (gmpi_context (mpi), service_id, 50);
  C_ASSERT_TRUE ( gserv );

  nOK = 1;
  
  gmpi_sync (mpi, GIGI_AND, GIGI_TYPE_INT32, &nOK, NULL);
  
  C_ASSERT_TRUE ( nOK );

  if ( id == 0 )
  {
    GIGI_ID addr;
    GIGI_ID nodes[128];
    GIGI_SOCKET fd;

    addr = gmpi_get_node_id (mpi, 1);

    fd = gmpi_remote_call (
        "BaseComm", 
        GIGI_ID_IP (addr), 
        GIGI_ID_PORT (addr), 
        service_id
    );

    C_ASSERT_TRUE ( fd );

    gmpi_get_nodes_of_group (mpi, nodes , GIGI_COUNT_OF (nodes) );

    gigi_send_object (fd, GIGI_TYPE_ID | GIGI_TYPE_ARRAY, nodes + 1, nSize - 1 );
    gigi_send_object (fd, GIGI_TYPE_ID, &service_id, 0 );

    gigi_closesocket (fd, GIGI_TRUE);
  }
  else
  {
    gmpi_service ( gmpi_context (mpi), param, service_id, -1);
  }

  gigi_close_service (gserv);
}


void TYPE_ARGV (GIGI_MPI mpi, void * param)
{
  int id = gmpi_rank (mpi);
  int n;

  const char * s[] = 
  {
    "a c sdf dfasf asdfas", 
    "", 
    "a", 
    "a b", 
    "asef 2 sdf sadf ", 
    NULL
  };

  const char ** pS;
  
  for ( pS = s; *pS; pS++)
  {
    char ** LocalList = strings_split (*pS, ' ');
    int    LocalListCount = strings_count ( (const char**)LocalList);

    char ** List      = id == 0 ? LocalList : NULL;
    int    ListCount = id == 0 ? LocalListCount : 0;

    if ( id != 0 )
    {
      free (LocalList);
    };

    n = gmpi_bcast (mpi, GIGI_TYPE_ARGV, &List, &ListCount, 0);

    C_ASSERT_TRUE (n >= 0);
    C_ASSERT_TRUE (ListCount == LocalListCount);

    if ( id != 0)
    {
      free (List);
    }

    n = gmpi_bcast (mpi, GIGI_TYPE_ARGV, &List, 0, 0);
    ListCount = strings_count ( (const char **)List);
    C_ASSERT_TRUE (ListCount == LocalListCount);

    free (List);
  }
};

void StreamBcast (GIGI_MPI mpi, void * param)
{
  GIGI_STREAM fp = NULL;
  int id = gmpi_rank (mpi);
  char fileName[256];
  
  GenTempFileName (fileName, sizeof (fileName) );
  
  // Create File at Node 0;
  //
  if ( id == 0)
  {
    C_ASSERT_TRUE ( GenTempFile (fileName, 64 * 1024 ) >= 0 );
  }
  else
  {
    strcat (fileName, "_bcast");
  }

  fp = gigi_fopen (fileName, id == 0 ? "r" : "w" );

  gmpi_bcast (mpi, GIGI_TYPE_STREAM, fp, NULL, 0);

  C_ASSERT_TRUE (fp);
  
  gigi_fclose (fp);

  remove_file (fileName);
};

COMM_TEST (types, StreamSendRecv, 2)
{
  GIGI_STREAM fp = NULL;
  GIGI_SOCKET fd;
  char fileName[256];
  
  GenTempFileName (fileName, sizeof (fileName) );
  
  // Create File at Node 0;
  //
  if ( ctx->id == 0)
  {
    C_ASSERT_TRUE ( GenTempFile (fileName, 64 * 1024 ) >= 0 );
  }
  else
  {
    strcat (fileName, "_bcast");
  }

  fp = gigi_fopen (fileName, ctx->id == 0 ? "r" : "w" );
  C_ASSERT_TRUE (fp);

  fd = create_channel (ctx, 0, 1);
  C_ASSERT_TRUE (fp);

  C_ASSERT_TRUE ( sendrecv (fd, GIGI_TYPE_STREAM, fp, NULL, ctx->id, 0, 1) > 0 );

  
  gigi_fclose (fp);

  remove_file (fileName);

  gigi_closesocket (fd, 1);
}

COMM_TEST (types, TYPE_String, 2)
{
  
};

COMM_TEST (types, TYPE_Buffer, 2)
{
  GIGI_SOCKET fd = create_channel (ctx, 0, 1);
  char sBuf[256];
  char * pBuf = sBuf;
  int i, nDataSize;

  C_ASSERT_TRUE ( fd );

  for (i = 0; i< sizeof (sBuf); i ++)
  {
    sBuf[i] = (char)i;
  }

  nDataSize = sizeof (sBuf);

  C_ASSERT_TRUE ( sendrecv (fd, GIGI_TYPE_BUFFER, pBuf, &nDataSize, ctx->id, 0, 1) > 0 );

  C_ASSERT_TRUE (nDataSize == sizeof (sBuf) );

  nDataSize = ctx->id == 0 ? 10 : 200;

  C_ASSERT_TRUE ( sendrecv (fd, GIGI_TYPE_BUFFER, pBuf, &nDataSize, ctx->id, 0, 1) > 0 );

  C_ASSERT_TRUE (nDataSize == 10 );

  nDataSize = ctx->id == 0 ? 200 : 10;

  C_ASSERT_TRUE ( sendrecv (fd, GIGI_TYPE_BUFFER, pBuf, &nDataSize, ctx->id, 0, 1) > 0 );

  gigi_closesocket (fd, 1);
}

C_TEST_CASE_BEGIN (service_testCases)
  C_TEST_CASE (baseMPI, PiCalc)
  C_TEST_CASE (baseMPI, BaseComm)
  C_TEST_CASE (baseMPI, tpsTest )
  C_TEST_CASE (RemoteCall, RemoteService)
  C_TEST_CASE (types, StreamBcast)
  C_TEST_CASE (types, StreamSendRecv)
  C_TEST_CASE (types, TYPE_ARGV)
  C_TEST_CASE (types, TYPE_Buffer)
C_TEST_CASE_END ()


